﻿using CodeBuilderForVS2022.Entity;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using RazorEngine;
using RazorEngine.Configuration;
using RazorEngine.Templating;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Engine = RazorEngine.Engine;
using Task = System.Threading.Tasks.Task;

namespace CodeBuilderForVS2022
{
    /// <summary>
    /// Command handler
    /// </summary>
    internal sealed class FlyadminCodeBuilderCommand
    {
        public static DTE2 _dte;

        /// <summary>
        /// Command ID.
        /// </summary>
        public const int CommandId = 0x0100;

        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid CommandSet = new Guid("9f15e097-b3b1-494e-912e-3b18cbc7af85");

        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly AsyncPackage package;

        private static string baseDic;

        /// <summary>
        /// Initializes a new instance of the <see cref="FlyadminCodeBuilderCommand"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        /// <param name="commandService">Command service to add command to, not null.</param>
        private FlyadminCodeBuilderCommand(AsyncPackage package, OleMenuCommandService commandService)
        {
            //InitRazorEngine();
            this.package = package ?? throw new ArgumentNullException(nameof(package));
            commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));

            var menuCommandID = new CommandID(CommandSet, CommandId);
            var menuItem = new MenuCommand(this.Execute, menuCommandID);
            commandService.AddCommand(menuItem);
        }

        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static FlyadminCodeBuilderCommand Instance
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the service provider from the owner package.
        /// </summary>
        private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
        {
            get
            {
                return this.package;
            }
        }

        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static async Task InitializeAsync(AsyncPackage package, DTE2 dte)
        {
            // Switch to the main thread - the call to AddCommand in FlyadminCodeBuilderCommand's constructor requires
            // the UI thread.
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
            _dte = dte;
            OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
            Instance = new FlyadminCodeBuilderCommand(package, commandService);
        }


        /// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void Execute(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            Main form1 = new Main("", "", _dte);

            ////加载动画
            LoadingForm loadingForm = new LoadingForm();
            loadingForm.Show();
            try
            {
                if (_dte.SelectedItems.Count == 1)
                {

                    SelectedItem selectedItem = _dte.SelectedItems.Item(1);
                    ProjectItem selectProjectItem = selectedItem.ProjectItem;
                    //获取当前点击的类所在的项目
                    Project entityProject = selectProjectItem.ContainingProject;

                   string baseDic = GetApiDir(entityProject);
                    EntityHelper entityHelper = new EntityHelper(baseDic, _dte);

                    ////当前类在当前项目中的目录结构
                    string dirPath = entityHelper.GetSelectFileDirPath(entityProject, selectProjectItem);
                    //当前类命名空间
                    string namespaceStr = selectProjectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>().First().FullName;
                    //当前项目根命名空间
                    string applicationStr = "";
                    if (!string.IsNullOrEmpty(namespaceStr))
                    {
                        applicationStr = namespaceStr.Substring(0, namespaceStr.IndexOf("."));
                    }
                    //当前类
                    CodeClass codeClass = entityHelper.GetClass(selectProjectItem.FileCodeModel.CodeElements);
                    //当前项目类名
                    string className = codeClass.Name;
                    string cnName = codeClass.DocComment.Replace("<doc>", "").Replace("<summary>", "").Replace("</doc>", "").Replace("</summary>", "").Replace("\r\n", "");


                    #region 错误提示
                    if (!ConfigHelper.IsFlyadminApi(baseDic))
                    {
                        loadingForm.ShowError("本代码生成器只适用于FlyAdmin.WebApi");
                        return;
                    }
                    if (!ConfigHelper.IsDataTable(baseDic, namespaceStr))
                    {
                        loadingForm.ShowError("请选择需要生成的数据表实体类");
                        return;
                    }
                    #endregion

                    loadingForm.ShowLoading("正在生成Input", 1);
                    //生成DTO
                    var inputProjectItem = entityProject.ProjectItems.Item("Input").ProjectItems.Item(className);
                    if (inputProjectItem == null)
                    {
                        inputProjectItem = entityProject.ProjectItems.Item("Input").ProjectItems.AddFolder(className);
                    }
                    DtoFileModel dtoModel = entityHelper.GetDtoModel(applicationStr, className, cnName, cnName, dirPath, codeClass);
                    entityHelper.CreateInputFile(dtoModel, className, inputProjectItem, loadingForm);

                    var outputProjectItem = entityProject.ProjectItems.Item("Output").ProjectItems.Item(className);
                    if (outputProjectItem == null)
                    {
                        outputProjectItem = entityProject.ProjectItems.Item("Output").ProjectItems.AddFolder(className);
                    }
                    entityHelper.CreateOutputFile(dtoModel, className, outputProjectItem, loadingForm);

                    entityHelper.CreateServiceFile(applicationStr, className, cnName, dirPath, loadingForm, codeClass);


                    loadingForm.ShowLoading($"正在编辑Mapper文件", 15);
                    entityHelper.SetMapper(entityProject, className, cnName);

                    loadingForm.ShowLoading($"正在编辑GlobalUsing文件", 16);
                    entityHelper.SetGlobalUsing(applicationStr, className);
                    loadingForm.ShowLoading($"代码生成完毕！", -1);
                }
                else
                {
                    loadingForm.ShowError("请选择需要生成的数据表实体类");
                }
            }
            catch (Exception ex)
            {
                loadingForm.ShowError(ex.Message);
            }
        }

        /// <summary>
        /// 获取Api所在的目录
        /// </summary>
        /// <param name="topProject"></param>
        /// <returns></returns>
        public string GetApiDir(Project topProject)
        {
            var arr = topProject.FileName.Split('\\');
            string projFullName = arr[arr.Length - 2];
            string projName = projFullName.Split('.')[1];
            projFullName = projFullName.Replace(projName, "WebApi");
            var newList = arr.ToList();
            newList.RemoveAt(arr.Length - 1);
            newList.RemoveAt(arr.Length - 2);
            newList.Add(projFullName);
            return string.Join("\\", newList);
        }
    }
}
